package cn.xinfei.xdecision.engine.runner.expressions;

import cn.xinfei.xdecision.common.model.enginex.runner.ExpressionParam;
import cn.xinfei.xdecision.common.utils.constant.runner.ParamTypeConst;
import cn.xinfei.xdecision.common.utils.util.runner.StrUtils;
import cn.xinfei.xdecision.common.utils.util.runner.jeval.EvaluationException;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.context.AmmunitionDepot;
import cn.xinfei.xdecision.nacos.NacosSwitchProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ExpressExecutor {


    public final static Map<String, FieldInfoVo> getFieldFromExpressField(String fieldValue) {
        JSONObject formulaJson = JSON.parseObject(fieldValue);
        //找到脚本中引用的字段,放入data中
        Object farr = formulaJson.get("farr");
        //字段cn为key，字段en为value
        Map<String, FieldInfoVo> fieldMap = new HashMap<>();
        if (farr != null && !"".equals(farr)) {
            List<FieldInfoVo> fieldList = JSONArray.parseArray(JSON.toJSONString(farr), FieldInfoVo.class);
            for (FieldInfoVo field : fieldList) {
                String fieldCn = field.getFieldName();
                String fieldEn = field.getFieldCode();
                if (fieldCn != null && fieldEn != null && !"".equals(fieldCn) && !"".equals(fieldEn)) {
                    fieldMap.put(fieldCn, field);
                }
            }
        }
        return fieldMap;
    }

    //处理集合中的特殊自定义
//    public final static Map<String, FieldInfoVo> getFieldFromExpressField(String fieldValue, List<JSONObject> current) {
//        JSONObject formulaJson = JSON.parseObject(fieldValue);
//        //找到脚本中引用的字段,放入data中
//        Object farr = formulaJson.get("farr");
//        //字段cn为key，字段en为value
//        Map<String, FieldInfoVo> fieldMap = new HashMap<>();
//        if (farr != null && !"".equals(farr)) {
//            List<JSONObject> fieldList = JSONArray.parseArray(JSON.toJSONString(farr), JSONObject.class);
//            for (JSONObject jsonObject : fieldList) {
//                String fieldCn = jsonObject.getString("fieldCn");
//                String fieldEn = jsonObject.getString("fieldEn");
//                FieldInfoVo field = new FieldInfoVo();
//                field.setFieldCode(fieldEn);
//                field.setFieldName(fieldCn);
//                field.setValueType(jsonObject.getInteger("valueType"));
//                Object thisFieldEnValue = null;
//                if (fieldCn != null && fieldEn != null && !"".equals(fieldCn) && !"".equals(fieldEn)) {
//                    fieldMap.put(fieldCn, field);
//                }
//                if (jsonObject.containsKey("temp") && jsonObject.getBoolean("temp")) {
//                    continue;
//                }
//                if (!jsonObject.containsKey("paramList")) {
//                    continue;
//                }
//
//                //存在paramList证明是参数绑定过的需要单个字段取出
//                JSONArray paramList = jsonObject.getJSONArray("paramList");
//                //input中根据fieldEn和绑定参数设置了缓存，此缓存需要规则中管理缓存层级。
//                String thisFieldEnKey = MD5.GetMD5Code(fieldEn + ":" + paramList.toJSONString());
//                if (PipelineContextHolder.containsKey(thisFieldEnKey)) {
//                    thisFieldEnValue = PipelineContextHolder.getValueByKey(thisFieldEnKey);
//                } else {
//                    for (int i = 0; i < paramList.size(); i++) {
//                        JSONObject param = paramList.getJSONObject(i);
//                        String paramEn = param.getString("en");
//                        String paramValue = param.getString("value");
//                        switch (param.getIntValue("type")) {
//                            case ParamTypeConst.CONSTANT:
//                                PipelineContextHolder.putInContext(paramEn, paramValue);
//                                break;
//                            case ParamTypeConst.VARIABLE:
//                                PipelineContextHolder.putInContext(paramEn, DataCleanUtils.getObjByKeyAndJson(current.get(0), paramValue));
//                                break;
//                        }
//                    }
//                    thisFieldEnValue = PipelineContextHolder.getValueByKey(fieldEn);
//                    PipelineContextHolder.putInContext(thisFieldEnKey, thisFieldEnValue);
//                }
//                PipelineContextHolder.putInContext(fieldEn, thisFieldEnValue);
//            }
//        }
//        return fieldMap;
//    }


    //获取基本单元的执行结果
    public final static boolean getExpressionResult(ExpressionParam expressionParam) throws EvaluationException {
        //如果是规则的条件的话，判断是否为叶子节点，如果不是则直接返回false
        if (expressionParam.getConditionType() != null && expressionParam.getConditionType() != 2) {
            return false;
        }
        String operator = expressionParam.getOperator();

        //获取第二个参数的类型
        Integer variableType = expressionParam.getValueType();
        //给每个参数取值
        String fieldEn = expressionParam.getFieldEn();
        //左侧的取值方式肯定是变量
        Object paramOne = AmmunitionDepot.getValueByKeyFromRemote(ParamTypeConst.VARIABLE, fieldEn, fieldEn);
        //默认为常量，有可能是变量。如果是常量fieldValue是具体值，如果是变量fieldValue是fieldCode
        String fieldValue = expressionParam.getFieldValue();
        Object paramTwo = null;
        if(ParamTypeConst.CONSTANT==variableType){
             paramTwo = AmmunitionDepot.getValueByKeyFromRemote(variableType, fieldEn, fieldValue);//用fieldEn类型进行cast
        }else if(ParamTypeConst.VARIABLE==variableType){
             paramTwo = AmmunitionDepot.getValueByKeyFromRemote(variableType, fieldValue, fieldValue);
        }else{
             paramTwo = AmmunitionDepot.getValueByKeyFromRemote(variableType, fieldValue, fieldValue);
        }

        if (NacosSwitchProperties.getNacosSwitchProperties().isDebugLog()) {
            log.info("fieldEn={},leftValue={}, operator={},rightValue={}", fieldEn, paramOne, operator, paramTwo);
        }
//        if (paramOne == null || "".equals(paramOne) || paramTwo == null || "".equals(paramTwo)) {
        if (paramOne == null || paramTwo == null) {
            log.warn("没有获取到参数值，{}={},{}={}", fieldEn, fieldValue, paramOne, paramTwo);
            return false;
        }
        if (paramOne.equals("\"\"")) {
            paramOne = "";
        }
        if (paramTwo.equals("\"\"")) {
            paramTwo = "";
        }

        return getCondResult(operator, paramOne, paramTwo);
    }


    //传入两个参数和一个操作符进行比对获取结果
    public final static boolean getCondResult(String operator, Object paramOne, Object paramTwo) {
        boolean result = false;
        //为什么转double? todo shiyusen
        Double numOne = StrUtils.strToDouble(paramOne.toString());
        Double numTwo = StrUtils.strToDouble(paramTwo.toString());
        switch (operator) {
            //数值之间的比较
            case "==":
                if (numOne != null && numTwo != null) {
                    result = numOne.equals(numTwo);
                } else if (paramOne != null && paramTwo != null) {
                    result = paramOne.toString().equals(paramTwo.toString());
                }
                break;
            case "!=":
                if (numOne != null && numTwo != null) {
                    result = !numOne.equals(numTwo);
                } else if (paramOne != null && paramTwo != null) {
                    result = !paramOne.toString().equals(paramTwo.toString());
                }
                break;
            case ">":
                if (numOne != null && numTwo != null) {
                    result = numOne > numTwo;
                }
                break;
            case "<":
                if (numOne != null && numTwo != null) {
                    result = numOne < numTwo;
                }
                break;
            case ">=":
                if (numOne != null && numTwo != null) {
                    result = numOne >= numTwo;
                }
                break;
            case "<=":
                if (numOne != null && numTwo != null) {
                    result = numOne <= numTwo;
                }
                break;
            //字符串之间的比较
            case "equals":
                result = paramOne.toString().equals(paramTwo.toString());
                break;
            case "not equals":
                result = !paramOne.toString().equals(paramTwo.toString());
                break;
            case "notEquals":
                result = !paramOne.toString().equals(paramTwo.toString());
                break;
            case "contains":
                result = paramOne.toString().contains(paramTwo.toString());
                break;
            case "not contains":
                result = !paramOne.toString().contains(paramTwo.toString());
                break;
            case "notContains":
                result = !paramOne.toString().contains(paramTwo.toString());
                break;
            case "regex":
                Pattern pattern = Pattern.compile(paramTwo.toString());
                Matcher matcher = pattern.matcher(paramOne.toString());
                result = matcher.find();
                break;
            case "in":
                if (paramTwo instanceof List) {
                    List list = (List) paramTwo;
                    result = list.contains(paramOne);
                } else if (paramTwo instanceof Map) {
                    Map map = (Map) paramTwo;
                    result = map.containsKey(paramOne);
                }
                break;
            case "not in":
                if (paramTwo instanceof List) {
                    List list = (List) paramTwo;
                    result = !list.contains(paramOne);
                } else if (paramTwo instanceof Map) {
                    Map map = (Map) paramTwo;
                    result = !map.containsKey(paramOne);
                }
                break;
            //数组之间的比较,
            // 包含任意一个
            case "array contains":
                JSONArray oneArray = JSON.parseArray(paramOne.toString());
                JSONArray twoArray;
                if (paramTwo != null) {
                    twoArray = (JSONArray) JSONArray.toJSON(paramTwo.toString().split(","));
                } else {
                    return false;
                }
                List<String> oneList = oneArray.toJavaList(String.class);
                List<String> twoList = twoArray.toJavaList(String.class);
                //包含任意一个，则返回true
                result = twoList.stream().anyMatch(item -> oneList.contains(item));
                break;
            //包含所有
            case "array all contains":
                JSONArray oneAllArray = JSON.parseArray(paramOne.toString());
                JSONArray twoAllArray;
                if (paramTwo != null) {
                    twoAllArray = (JSONArray) JSONArray.toJSON(paramTwo.toString().split(","));
                } else {
                    return false;
                }
                List<String> oneAllList = oneAllArray.toJavaList(String.class);
                List<String> twoAllList = twoAllArray.toJavaList(String.class);
                //包含所有，则返回true
                result = twoAllList.stream().allMatch(item -> oneAllList.contains(item));
                break;
            //不包含所有
            case "array not contains":
                JSONArray oneArrayN = JSON.parseArray(paramOne.toString());
                JSONArray twoArrayN;
                if (paramTwo != null) {
                    twoArrayN = (JSONArray) JSONArray.toJSON(paramTwo.toString().split(","));
                } else {
                    return false;
                }
                List<String> oneListN = oneArrayN.toJavaList(String.class);
                List<String> twoListN = twoArrayN.toJavaList(String.class);
                //全不包含，则返回true
                result = twoListN.stream().allMatch(item -> !oneListN.contains(item));
                break;
        }
        return result;
    }

}
